/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package me.zhengjie.modules.system.service.impl;

import cn.hutool.core.util.StrUtil;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import me.zhengjie.config.FileProperties;
import me.zhengjie.exception.EntityExistException;
import me.zhengjie.exception.EntityNotFoundException;
import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.system.repository.UserRepository;
import me.zhengjie.modules.system.service.UserService;
import me.zhengjie.modules.system.service.dto.JobSmallDto;
import me.zhengjie.modules.system.service.dto.RoleSmallDto;
import me.zhengjie.modules.system.service.dto.UserDto;
import me.zhengjie.modules.system.service.dto.UserQueryCriteria;
import me.zhengjie.modules.system.service.mapstruct.UserMapper;
import me.zhengjie.utils.FileUtil;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.QueryHelp;
import me.zhengjie.utils.RedisUtils;
import me.zhengjie.utils.SecurityUtils;
import me.zhengjie.utils.ValidationUtil;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author Zheng Jie
 * @date 2018-11-23
 */
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "user")
public class UserServiceImpl implements UserService {

  private final UserRepository userRepository;
  private final UserMapper userMapper;
  private final FileProperties properties;
  private final RedisUtils redisUtils;

  @Override
  public Object queryAll(UserQueryCriteria criteria, Pageable pageable) {
    Page<User> page = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
    return PageUtil.toPage(page.map(userMapper::toDto));
  }

  @Override
  public List<UserDto> queryAll(UserQueryCriteria criteria) {
    List<User> users = userRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder));
    return userMapper.toDto(users);
  }

  @Override
  @Cacheable(key = "'id:' + #p0")
  @Transactional(rollbackFor = Exception.class)
  public UserDto findById(long id) {
    User user = userRepository.findById(id).orElseGet(User::new);
    ValidationUtil.isNull(user.getId(),"User","id",id);
    return userMapper.toDto(user);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void create(User resources) {
    if(userRepository.findByUsername(resources.getUsername())!=null){
      throw new EntityExistException(User.class,"username",resources.getUsername());
    }
    if(userRepository.findByEmail(resources.getEmail())!=null){
      throw new EntityExistException(User.class,"email",resources.getEmail());
    }
    userRepository.save(resources);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void update(User resources) {
    User user = userRepository.findById(resources.getId()).orElseGet(User::new);
    ValidationUtil.isNull(user.getId(),"User","id",resources.getId());
    User user1 = userRepository.findByUsername(resources.getUsername());
    User user2 = userRepository.findByEmail(resources.getEmail());

    if(user1 !=null&&!user.getId().equals(user1.getId())){
      throw new EntityExistException(User.class,"username",resources.getUsername());
    }

    if(user2!=null&&!user.getId().equals(user2.getId())){
      throw new EntityExistException(User.class,"email",resources.getEmail());
    }
    // 如果用户的角色改变
    if (!resources.getRoles().equals(user.getRoles())) {
      redisUtils.del("data::user:" + resources.getId());
      redisUtils.del("menu::user:" + resources.getId());
      redisUtils.del("role::auth:" + resources.getId());
    }
    user.setUsername(resources.getUsername());
    user.setEmail(resources.getEmail());
    user.setEnabled(resources.getEnabled());
    user.setRoles(resources.getRoles());
    user.setDept(resources.getDept());
    user.setJobs(resources.getJobs());
    user.setPhone(resources.getPhone());
    user.setNickName(resources.getNickName());
    user.setGender(resources.getGender());
    userRepository.save(user);
    // 清除缓存
    delCaches(user.getId(), user.getUsername());
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void updateCenter(User resources) {
    User user = userRepository.findById(resources.getId()).orElseGet(User::new);
    user.setNickName(resources.getNickName());
    user.setPhone(resources.getPhone());
    user.setGender(resources.getGender());
    userRepository.save(user);
    // 清理缓存
    delCaches(user.getId(), user.getUsername());
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void delete(Set<Long> ids) {
    for (Long id : ids) {
      // 清理缓存
      UserDto user = findById(id);
      delCaches(user.getId(), user.getUsername());
    }
    userRepository.deleteAllByIdIn(ids);
  }

  @Override
  @Cacheable(key = "'username:' + #p0")
  public UserDto findByName(String userName) {
    User user = userRepository.findByUsername(userName);
    if (user == null) {
      throw new EntityNotFoundException(User.class, "name", userName);
    } else {
      return userMapper.toDto(user);
    }
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void updatePass(String username, String pass) {
    userRepository.updatePass(username,pass,new Date());
    redisUtils.del("user::username:" + username);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public Map<String, String> updateAvatar(MultipartFile multipartFile) {
    User user = userRepository.findByUsername(SecurityUtils.getCurrentUsername());
    String oldPath = user.getAvatarPath();
    File file = FileUtil.upload(multipartFile, properties.getPath().getAvatar());
    user.setAvatarPath(Objects.requireNonNull(file).getPath());
    user.setAvatarName(file.getName());
    userRepository.save(user);
    if(StrUtil.isNotBlank(oldPath)){
      FileUtil.del(oldPath);
    }
    redisUtils.del("user::username:" + user.getUsername());
    return new HashMap<String,String>(1){{put("avatar",file.getName());}};
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void updateEmail(String username, String email) {
    userRepository.updateEmail(username,email);
    redisUtils.del("user::username:" + username);
  }

  @Override
  public void download(List<UserDto> queryAll, HttpServletResponse response) throws IOException {
    List<Map> list = new ArrayList<>();
    for (UserDto userDTO : queryAll) {
      List<String> roles = userDTO.getRoles().stream().map(RoleSmallDto::getName).collect(Collectors.toList());
      Map<String,Object> map = new LinkedHashMap<>();
      map.put("用户名", userDTO.getUsername());
      map.put("角色", roles);
      map.put("部门", userDTO.getDept().getName());
      map.put("岗位", userDTO.getJobs().stream().map(JobSmallDto::getName).collect(Collectors.toList()));
      map.put("邮箱", userDTO.getEmail());
      map.put("状态", userDTO.getEnabled() ? "启用" : "禁用");
      map.put("手机号码", userDTO.getPhone());
      map.put("修改密码的时间", userDTO.getPwdResetTime());
      map.put("创建日期", userDTO.getCreateTime());
      list.add(map);
    }
    FileUtil.downloadExcel(list, response);
  }

  /**
   * 清理缓存
   * @param id /
   */
  public void delCaches(Long id, String username){
    redisUtils.del("user::id:" + id);
    redisUtils.del("user::username:" + username);
  }
}
